package cn.tannn.ops.util;


import cn.tannn.jdevelops.exception.built.BusinessException;
import cn.tannn.ops.nginx.entity.CloudAccessKey;
import com.alibaba.fastjson2.JSON;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.alidns20150109.AsyncClient;
import com.aliyun.sdk.service.alidns20150109.models.*;
import darabonba.core.client.ClientOverrideConfiguration;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.ExecutionException;


/**
 * 阿里dns工具
 *
 * @author <a href="https://api.alibabacloud.com/api-tools/sdk/Alidns?version=2015-01-09&language=java-async-tea&tab=primer-doc">...</a>
 * @version V1.0
 * @date 2024/8/29 上午9:17
 */
public class AliDnsUtil {
    // Aliyun DNS API credentials
    // https://ram.console.aliyun.com/users/detail?userId=203193816817432348&activeTab=PermissionList
    // 记得设置权限管理 - 管理云解析（DNS）的权限，管理公共DNS（PubDNS）的权限，管理HTTPDNS的权限
    private static final Logger LOG = LoggerFactory.getLogger(AliDnsUtil.class);

    /**
     * Init 初始化客户端
     *
     * @param cloud 密钥数据
     * @return Client
     */
    @SneakyThrows
    public static AsyncClient init(CloudAccessKey cloud) {
        return init(cloud.getAccessId(), cloud.getAccessSecret());
    }

    /**
     * Init 初始化客户端
     * <p>
     * 启动的时候添加环境变量  ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET
     * </p>
     *
     * @return Client
     */
    @SneakyThrows
    public static AsyncClient init() {
        String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
        String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        return init(accessKeyId, accessKeySecret);
    }

    /**
     * Init 初始化客户端
     *
     * @param accessKeyId     密钥Id
     * @param accessKeySecret 密钥
     * @return Client
     */
    @SneakyThrows
    public static AsyncClient init(String accessKeyId, String accessKeySecret) {
        // HttpClient Configuration
        /*HttpClient httpClient = new ApacheAsyncHttpClientBuilder()
                .connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds
                .responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds
                .maxConnections(128) // Set the connection pool size
                .maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds
                // Configure the proxy
                .proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<your-proxy-hostname>", 9001))
                        .setCredentials("<your-proxy-username>", "<your-proxy-password>"))
                // If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true))
                .x509TrustManagers(new X509TrustManager[]{})
                .keyManagers(new KeyManager[]{})
                .ignoreSSL(false)
                .build();*/
        // Configure Credentials authentication information, including ak, secret, token
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                // Please ensure that the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set.
                .accessKeyId(accessKeyId)
                .accessKeySecret(accessKeySecret)
                //.securityToken(System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // use STS token
                .build());
        return AsyncClient.builder()
                //.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)
                .credentialsProvider(provider)
                //.serviceConfiguration(Configuration.create()) // Service-level configuration
                // Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
                .overrideConfiguration(
                        ClientOverrideConfiguration.create()
                                // Endpoint 请参考 https://api.aliyun.com/product/Alidns
                                .setEndpointOverride("alidns.cn-hangzhou.aliyuncs.com")
                        //.setConnectTimeout(Duration.ofSeconds(30))
                )
                .build();
    }

    /**
     * DescribeDomains  查询账户下域名
     *
     * @param client 客户端
     */
    @SneakyThrows
    public static DescribeDomainsResponse describeDomains(AsyncClient client) {

        LOG.info("查询域名列表(json)↓");
        try {
            DescribeDomainsRequest req = DescribeDomainsRequest.create();
            DescribeDomainsResponse resp = client.describeDomains(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("查询域名列表:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * AddDomain  阿里云云解析添加域名
     *
     * @param client     客户端
     * @param domainName 域名名称
     */
    @SneakyThrows
    public static AddDomainResponse addDomain(AsyncClient client, String domainName) {
        AddDomainRequest req = AddDomainRequest.builder().domainName(domainName).build();
        LOG.info("云解析添加域名({})的结果(json)↓", domainName);
        try {
            AddDomainResponse resp = client.addDomain(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("云解析添加域名:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * DescribeDomainRecords 查询域名解析记录
     *
     * @param client     客户端
     * @param domainName 域名名称
     */
    public static DescribeDomainRecordsResponse describeDomainRecords(AsyncClient client, String domainName) {
        return describeDomainRecords(client, domainName, 500L);
    }

    /**
     * DescribeDomainRecords 查询域名解析记录
     *
     * @param client     客户端
     * @param domainName 域名名称
     * @param pageSize   分页大小【最大500】
     */
    @SneakyThrows
    public static DescribeDomainRecordsResponse describeDomainRecords(AsyncClient client, String domainName, Long pageSize) {
        DescribeDomainRecordsRequest req = DescribeDomainRecordsRequest.builder()
                .pageSize(pageSize)
                .domainName(domainName)
                .build();
        LOG.info("查询域名({})的解析记录(json)↓", domainName);
        try {
            DescribeDomainRecordsResponse resp = client.describeDomainRecords(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("查询域名:{}", domainName, error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * DescribeRecordLogs  查询域名解析记录日志
     *
     * @param client     客户端
     * @param domainName 域名名称
     */
    @SneakyThrows
    public static DescribeRecordLogsResponse describeRecordLogs(AsyncClient client, String domainName) {
        DescribeRecordLogsRequest req = DescribeRecordLogsRequest.builder()
                .domainName(domainName)
                .build();
        LOG.info("查询域名({})的解析记录(json)↓", domainName);
        try {
            DescribeRecordLogsResponse resp = client.describeRecordLogs(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("查询域名:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * DescribeDomainRecordByRecordId 查询域名解析记录信息
     *
     * @param client   客户端
     * @param recordId 解析记录id
     */
    @SneakyThrows
    public static DescribeDomainRecordInfoResponse describeDomainRecordByRecordId(AsyncClient client, String recordId) {
        DescribeDomainRecordInfoRequest req = DescribeDomainRecordInfoRequest.builder()
                .recordId(recordId)
                .build();
        LOG.info("查询RecordId:{}的域名解析记录信息(json)↓", recordId);
        try {
            DescribeDomainRecordInfoResponse resp = client.describeDomainRecordInfo(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("查询RecordId:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * DescribeDomainInfo  查询域名信息
     *
     * @param client     客户端
     * @param domainName 域名名称
     */
    @SneakyThrows
    public static DescribeDomainInfoResponse describeDomainInfo(AsyncClient client, String domainName) {
        DescribeDomainInfoRequest req = DescribeDomainInfoRequest.builder()
                .domainName(domainName)
                .build();
        LOG.info("查询域名:{}的信息(json)↓", domainName);
        try {
            DescribeDomainInfoResponse resp = client.describeDomainInfo(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("查询域名:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * AddDomainRecord  添加域名解析记录
     * <code>
     * <p> DomainExtractor.DomainInfo domainInfo = DomainExtractor.extractDomainInfo(domainName);
     * <p> String rr = Dns01Challenge.toRRName(auth.getIdentifier()).replace("." + domainInfo.mainDomain + ".", "");
     * <p> String domainName = domainInfo.mainDomain;
     * </code>
     *
     * @param client     客户端
     * @param domainName 域名名称 (子域名的时候这里填写的是主域名)
     * @param rr         主机记录(子域名前缀 e.g xx.tan.cn 中的tan)
     * @param recordType 记录类型(A/NS/MX/TXT/CNAME/SRV/AAAA/CAA/REDIRECT_URL/FORWARD_URL)
     * @param value      记录值
     */
    @SneakyThrows
    public static AddDomainRecordResponse addDomainRecord(AsyncClient client, String domainName, String rr, String recordType, String value) {
        LOG.info("添加域名解析记录的结果(json)↓ domainName={}, rr={}, recordType={},value={}"
                , domainName
                , rr
                , recordType
                , value);
        AddDomainRecordRequest req = AddDomainRecordRequest.builder()
                .domainName(domainName)
                .rr(rr)
                .type(recordType)
                .value(value)
                .build();
        try {
            AddDomainRecordResponse resp = client.addDomainRecord(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("添加域名解析记录的结果:", error);
            throw error;
        } finally {
//            client.close();
        }
    }

    /**
     * UpdateDomainRecord  更新域名解析记录
     *
     * @param client     客户端
     * @param recordId   解析记录ID
     * @param rr         主机记录
     * @param recordType 记录类型(A/NS/MX/TXT/CNAME/SRV/AAAA/CAA/REDIRECT_URL/FORWARD_URL)
     * @param value      记录值
     */
    @SneakyThrows
    public static UpdateDomainRecordResponse updateDomainRecord(AsyncClient client, String recordId, String rr, String recordType, String value) {
        UpdateDomainRecordRequest req = UpdateDomainRecordRequest.builder()
                .recordId(recordId)
                .rr(rr)
                .type(recordType)
                .value(value)
                .build();
        LOG.info("更新域名解析记录的结果(json)↓");
        try {
            UpdateDomainRecordResponse resp = client.updateDomainRecord(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("更新域名解析记录的结果:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * SetDomainRecordStatus  设置域名解析状态
     *
     * @param client   客户端
     * @param recordId 解析记录ID
     * @param status   解析状态(ENABLE/DISABLE)
     */
    @SneakyThrows
    public static SetDomainRecordStatusResponse setDomainRecordStatus(AsyncClient client, String recordId, String status) {
        SetDomainRecordStatusRequest req = SetDomainRecordStatusRequest.builder()
                .recordId(recordId)
                .status(status)
                .build();
        LOG.info("设置域名解析状态的结果(json)↓");
        try {
            SetDomainRecordStatusResponse resp = client.setDomainRecordStatus(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("设置域名解析状态的结果:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * DeleteDomainRecord  删除域名解析记录
     *
     * @param client   客户端
     * @param recordId 解析记录ID
     */
    @SneakyThrows
    public static DeleteDomainRecordResponse deleteDomainRecord(AsyncClient client, String recordId) {
        DeleteDomainRecordRequest req = DeleteDomainRecordRequest.builder()
                .recordId(recordId)
                .build();
        LOG.info("删除域名解析记录的结果(json)↓");
        try {
            DeleteDomainRecordResponse resp = client.deleteDomainRecord(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("删除域名解析记录的结果:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * DescribeDomainGroups  查询域名组
     *
     * @param client 客户端
     */
    @SneakyThrows
    public static DescribeDomainGroupsResponse describeDomainGroups(AsyncClient client) {
        DescribeDomainGroupsRequest req = DescribeDomainGroupsRequest.builder()
                .build();
        LOG.info("查询域名组(json)↓");
        try {
            DescribeDomainGroupsResponse resp = client.describeDomainGroups(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("查询域名组:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * AddDomainGroup  添加域名组
     *
     * @param client    客户端
     * @param groupName 域名组名
     */
    @SneakyThrows
    public static AddDomainGroupResponse AddDomainGroup(AsyncClient client, String groupName) {
        AddDomainGroupRequest req = AddDomainGroupRequest.builder()
                .groupName(groupName)
                .build();
        LOG.info("添加域名组的结果(json)↓");
        try {
            AddDomainGroupResponse resp = client.addDomainGroup(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("添加域名组的结果:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * UpdateDomainGroup  更新域名组名称
     *
     * @param client    客户端
     * @param groupId   解析组ID
     * @param groupName 新域名组名称
     */
    @SneakyThrows
    public static UpdateDomainGroupResponse updateDomainGroup(AsyncClient client, String groupId, String groupName) {
        UpdateDomainGroupRequest req = UpdateDomainGroupRequest.builder()
                .groupId(groupId)
                .groupName(groupName).build();
        LOG.info("更新域名组的结果(json)↓");
        try {
            UpdateDomainGroupResponse resp = client.updateDomainGroup(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("更新域名组的结果:", error);
            throw error;
        } finally {
            client.close();
        }
    }

    /**
     * DeleteDomainGroup  删除域名组
     *
     * @param client  客户端
     * @param groupId 域名组ID
     */
    @SneakyThrows
    public static DeleteDomainGroupResponse deleteDomainGroup(AsyncClient client, String groupId) {
        DeleteDomainGroupRequest req = DeleteDomainGroupRequest.builder()
                .groupId(groupId).build();
        LOG.info("删除域名组的结果(json)↓");
        try {
            DeleteDomainGroupResponse resp = client.deleteDomainGroup(req).get();
            if (LOG.isDebugEnabled()) {
//                TeaModel.buildMap(resp);
                LOG.debug(JSON.toJSONString(resp));
            }
            return resp;
        } catch (Exception error) {
            LOG.error("删除域名组的结果:", error);
            throw error;
        } finally {
            client.close();
        }
    }


    public static String getRecordIdByDomain(AsyncClient client, String domainName, String rr, String type, String value) throws Exception {
        DescribeDomainRecordsRequest describeDomainRecordsRequest = DescribeDomainRecordsRequest.builder()
                .domainName(domainName)
                .build();

        try {
            DescribeDomainRecordsResponse response = client.describeDomainRecords(describeDomainRecordsRequest).get();
            List<DescribeDomainRecordsResponseBody.Record> records = response.getBody().getDomainRecords().getRecord();
            for (DescribeDomainRecordsResponseBody.Record record : records) {
                if (record.getRr().equals(rr) && record.getType().equals(type) && record.getValue().equals(value)) {
                    return record.getRecordId();
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            LOG.error("Failed to retrieve domain records: " + e.getMessage(), e);
            throw new BusinessException("Failed to retrieve domain records", e);
        }
        return null;
    }
}
